package com.easy.clickhouse;

import com.easy.clickhouse.execption.ClickHouseOperateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.clickhouse.BalancedClickhouseDataSource;
import ru.yandex.clickhouse.ClickHouseConnection;
import ru.yandex.clickhouse.ClickHouseConnectionImpl;
import ru.yandex.clickhouse.ClickHouseStatement;
import ru.yandex.clickhouse.response.ClickHouseResponse;
import ru.yandex.clickhouse.settings.ClickHouseProperties;
import ru.yandex.clickhouse.settings.ClickHouseQueryParam;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author jetlag
 * @version 1.0.0
 * @date 2022/3/12 15:41
 **/
public class ClickHouseTemplate {

    private static Logger log = LoggerFactory.getLogger(ClickHouseTemplate.class);

    @Autowired
    private CKProperties ckProperties;

    @Autowired
    private BalancedClickhouseDataSource clickHouseDataSource;

    /**
     * 检查服务器是否存活
     * @param clickHouseServer
     * @return
     */
    public boolean serverIsAlive(ClickHouseServer clickHouseServer) {
        ClickHouseConnection connection = null;
        ClickHouseStatement statement = null;
        try {
            ClickHouseProperties clickProperties = ckProperties.getCluster().getClusterProperties();
            connection = new ClickHouseConnectionImpl(clickHouseServer.getUrl(), clickProperties);
            statement = connection.createStatement();
            statement.execute("SELECT 1");
            return true;
        } catch(Exception e) {
            log.error("clickhouse sql execute error...{}", e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("close clickhouse connection error...{}", e);
            }
        }
        return false;
    }

    /**
     * 随机负载执行
     * @param executeSql
     * @return
     */
    public int executeByBalance(String executeSql) {
        ClickHouseConnection connection = null;
        ClickHouseStatement statement = null;
        try {
            connection = clickHouseDataSource.getConnection();
            statement = connection.createStatement();
            return statement.executeUpdate(executeSql);
        } catch(Exception e) {
            log.error("clickhouse sql execute error...{}", e);
            throw new ClickHouseOperateException("clickhouse sql execute error...{}", e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("close clickhouse connection error...{}", e);
            }
        }
    }

    /**
     * 指定地址执行
     * @param clickHouseServer
     * @param executeSql
     * @return
     */
    public int executeBySpecify(ClickHouseServer clickHouseServer, String executeSql) {
        ClickHouseConnection connection = null;
        ClickHouseStatement statement = null;
        try {
            ClickHouseProperties clickProperties = ckProperties.getCluster().getClusterProperties();
            connection = new ClickHouseConnectionImpl(clickHouseServer.getUrl(), clickProperties);
            statement = connection.createStatement();
            return statement.executeUpdate(executeSql);
        } catch(Exception e) {
            log.error("clickhouse sql execute error...{}", e);
            throw new ClickHouseOperateException("clickhouse sql execute error...{}", e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("close clickhouse connection error...{}", e);
            }
        }
    }

    /**
     * 查询
     * @param querySql
     * @return
     */
    public List<Map<String, Object>> query(String querySql) {
        List<Map<String, Object>> results = new ArrayList<>();
        ClickHouseConnection connection = null;
        ClickHouseStatement statement = null;
        ClickHouseResponse response = null;
        try {
            connection = clickHouseDataSource.getConnection();
            statement = connection.createStatement();
            response = statement.executeQueryClickhouseResponse(querySql);
            List<ClickHouseResponse.Meta> metas = response.getMeta();
            List<List<String>> datas = response.getData();
            for (int i = 0; i < datas.size(); i++) {
                List<String> data = datas.get(i);
                Map<String, Object> object = new HashMap<>(metas.size() * 2);
                results.add(object);
                for (int j = 0; j < metas.size(); j++) {
                    object.put(metas.get(j).getName(), data.get(j));
                }
            }
        } catch(Exception e) {
            log.error("clickhouse sql query error...{}", e);
            throw new ClickHouseOperateException("clickhouse sql execute error...{}", e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("close clickhouse connection error...{}", e);
            }
        }
        return results;
    }

    /**
     * 带系统参数查询
     * @param querySql
     * @param params
     * @return
     */
    public List<Map<String, Object>> query(String querySql, Map<ClickHouseQueryParam, String> params) {
        List<Map<String, Object>> results = new ArrayList<>();
        ClickHouseConnection connection = null;
        ClickHouseStatement statement = null;
        ClickHouseResponse response = null;
        try {
            connection = clickHouseDataSource.getConnection();
            statement = connection.createStatement();
            response = statement.executeQueryClickhouseResponse(querySql, params);
            List<ClickHouseResponse.Meta> metas = response.getMeta();
            List<List<String>> datas = response.getData();
            for (int i = 0; i < datas.size(); i++) {
                List<String> data = datas.get(i);
                Map<String, Object> object = new HashMap<>(metas.size() * 2);
                results.add(object);
                for (int j = 0; j < metas.size(); j++) {
                    object.put(metas.get(j).getName(), data.get(j));
                }
            }
        } catch(Exception e) {
            log.error("clickhouse sql query error...{}", e);
            throw new ClickHouseOperateException("clickhouse sql execute error...{}", e);
        } finally {
            try {
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                log.error("close clickhouse connection error...{}", e);
            }
        }
        return results;
    }
}
